package com.my.jwt.shiro.thread;

import com.alibaba.fastjson.JSONObject;
import com.my.cache.bean.CacheBean;
import com.my.core.domain.exception.SecureException;
import com.my.core.util.spring.SpringUtil;
import com.my.jwt.shiro.thread.domain.SessionUser;
import com.my.jwt.shiro.thread.util.JwtUtil;
import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/** C 端 APP 用户登录
 * @Author: hjx
 * @Date: 2020/12/10 16:37
 */


@Component
public class AppLoginContext {
    /**
     * jwt 存储在缓存中的原因
     *
     * 的方式向后台发送请求，从而进行用户认证，是一种无状态的认证机制，而服务端将会检查请求头Authorization中的jwt信息，如果合法，则允许用户的行为。
     * 这样一来，jwt技术减少了大量查询数据库进行用户认证的需要。
     * 可是token信息是不保密的，一旦被产生泄露，就会产生安全风险，这时候是否需要将token存入redis中，进一步确保安全呢？ 对此，我挺纠结的，因为我感觉这违反了jwt技术减少数据库查询，减缓服务器压力的初衷。
     * 再举一个实例，我现在需要做一个用户的退出功能，按照jwt的原理，只需在前端localStorage中把token删除，从而使得页面向后台发送请求的请求头中没有token即可，可是这样一来，token还没有到过期时间，服务器端对token的认证还存在，此时也会产生安全风险。本人为了程序安全着想，把token存入了redis，在认证前判断redis中该用户的token是否存在，在做用户退出功能时，一同把redis数据库中的token删除，但我感觉这一系列操作违背了jwt技术的初衷，对此各位大佬们怎么看呢？
     * ————————————————
     * 版权声明：本文为CSDN博主「MrLi0104」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
     * 原文链接：https://blog.csdn.net/godHhh/article/details/111280724
     */
    @Autowired
    @Qualifier("redisCacheBean")
    protected CacheBean cacheBean;

    final static private String default_secret = "1336931509990330370";
    //登录超时时间
    public static  final  long C_EXPIRE_TIME = 30 * 24* 60 * 60 * 1000L;
    /**缓存key*/
    public final static String C_USERINFO_KEY = "C_APP_USER:";
    /**天府通办信息缓存*/
    public final static String T_USERINFO_KEY = "T_APP_USER:";
    /**前缀*/
    /**C 端用户登录 的 token 前缀*/
    public final static String APP_C_TOKEN_PFE = "APP_C_TOKEN:";
    /**天府通办 的 token 前缀*/
    public final static String APP_TFTB_TOKEN_PFE = "APP_T_TOKEN:";
    /**后缀*/
    /**C端用户token 后缀*/
    public final static String APP_C_TOKEN_SUFF = "$$$_C";
    /**天府通办端用户token 后缀*/
    public final static String APP_TFTB_TOKEN_SUFF = "$$$_T";

    public final static String REGEX = "\\$\\$\\$";
    //获取用户请求的token
    public String getReqToken(){
        return SpringUtil.getHttpServletRequest().getHeader("X-Access-Token");
    }
    /**
     * token 创建 并存入缓存
     * @param sessionUser 对象
     * @param tokenPfe 前缀
     * @param tokenSuff 后缀
     * @param cacheKey 缓存key
     * @return
     */
    public  String createToken(SessionUser sessionUser,String tokenPfe,String tokenSuff,String cacheKey){
        String token = JwtUtil.sign(sessionUser.getUsername(), default_secret,C_EXPIRE_TIME ) + tokenSuff;
        // 设置token缓存有效时间
        cacheBean.put(tokenPfe + token,token, (int) (C_EXPIRE_TIME / 1000));
        //缓存用户信息
        String jsonStr = JSONObject.toJSONString(sessionUser);
        cacheBean.put(cacheKey + sessionUser.getUsername(),jsonStr);
        return token;
    }

    /**
     * 判断token 是否为天府通 token
     * @param token
     * @return
     */
    public boolean isAppT(String token ){
        if (io.micrometer.core.instrument.util.StringUtils.isNotEmpty(token)){
            String arr[] = token.split(REGEX);
            if (null != arr && arr.length == 2){
                if ("_T".equals(arr[1])){
                    return true;
                }
            }
        }
        return false;
    }
    //该 token 是否为 C 端 APP token
    public boolean isAppC(String token ){
        if (StringUtils.isNotEmpty(token)){
            String arr[] = token.split(REGEX);
            if (null != arr && arr.length == 2){
                if ("_C".equals(arr[1])){
                    return true;
                }
            }
        }
        return false;
    }

    //解析token
    public SessionUser  parseTokenInfo(String token){
        String userName = JwtUtil.getUsername(token);
        String userKey = C_USERINFO_KEY + userName;
        //获取缓存信息
        Object obj = cacheBean.get(userKey);
        if (null == obj){
            throw  new SecureException("非法的token");
        }
        String str = obj.toString();
        SessionUser sessionUser =  JSONObject.parseObject(str,SessionUser.class);
        boolean f = jwtTokenRefresh(userName);
        if (!f){
            throw new SecureException("C 端解析失败");
        }
        return sessionUser;
    }


    /**
     * 刷新token
     * @param userName
     * @return
     */
    public boolean jwtTokenRefresh(String userName) {
        String reqToken = getReqToken();
        String cacheToken = String.valueOf(cacheBean.get(APP_C_TOKEN_PFE + reqToken));
        cacheToken = getNormalTokenIfAppCToken(cacheToken);
        //去点 后缀
        if (StringUtils.isNotEmpty(cacheToken)) {
            // 校验token有效性
            if (JwtUtil.verify(cacheToken, userName, default_secret)) {
                String newAuthorization = JwtUtil.sign(userName, default_secret);
                // 设置超时时间
                cacheBean.put(APP_C_TOKEN_PFE + reqToken, newAuthorization, (int) (C_EXPIRE_TIME *2 / 1000));
                return true;
            }else {
                throw  new SecureException("C 端登录,Token 校验不通过");
            }
        }
        return false;
    }
    public String getNormalTokenIfAppCToken(String token ){
        String reqToken = getReqToken();
        String arr[] = reqToken.split(REGEX);
        return arr[0];
    }
}
